﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// NotReviewedButNeeded
#pragma once
#include "GbmpAssert.h"
#include "OwnerPtr.h"

#include "NdbTypeTraits.h"

#include "NdbClassSchemaLibraryOfCode.h"
#include "NdbClassSchema.h"
#include "NdbPropertySchema.h"
#include "NdbDataChangeUtils.h"

#include "NdbVector.h"
#include "NdbMap.h"
#include "NdbUnorderedMap.h"
#include "NdbSet.h"
#include <type_traits>
#include "IDbObjectArchive.h"

EXPOSE_DEBUG_MODE_EXPORT(CheckErrorUseOfNonConstInterface, GCMP_DEV_SERVICE_EXPORT);

namespace gcmp
{
    class IElement;
    class IDocument;

    template<class T>
    void AssignValue_DONT_USE(T& var, const T& value)
    {
        var = value;
    }

    template<class T>
    void AssignValue_DONT_USE(T*& var, const T* value)
    {
        var = (T*)value;
    }

    template<class T>
    void AssignValue_DONT_USE(OwnerPtr<T>& var, const OwnerPtr<T>& value)
    {
        OwnerPtr<T>* popValue = const_cast<OwnerPtr<T>*>(&value);
        var = TransferOwnership(*popValue);
    }

    template<class T>
    void AssignValue_DONT_USE(gcmp::NdbVector<T>& var, const std::vector<T>& value)
    {
        var = value;
    }

    template<class T>
    void AssignValue_DONT_USE(gcmp::NdbVector<gcmp::OwnerPtr<T>>& var, const std::vector<gcmp::OwnerPtr<T>>& value)
    {
        DBG_WARN(L"包含gcmp::OwnerPtr的vector不支持直接赋值", L"GMEP", L"2016-09-14");
    }

    template<class T>
    void AssignValue_DONT_USE(gcmp::NdbSet<T>& var, const std::set<T>& value)
    {
        var = value;
    }

    template<class T>
    void AssignValue_DONT_USE(gcmp::NdbSet<gcmp::OwnerPtr<T>>& var, const std::set<gcmp::OwnerPtr<T>>& value)
    {
        DBG_WARN(L"包含gcmp::OwnerPtr的set不支持直接赋值", L"GMEP", L"2016-09-14");
    }

    template<class T, class Comparer>
    void AssignValue_DONT_USE(gcmp::NdbSet<T, Comparer>& var, const std::set<T, Comparer>& value)
    {
        var = value;
    }

    template<class T, class Comparer>
    void AssignValue_DONT_USE(gcmp::NdbSet<gcmp::OwnerPtr<T>, Comparer>& var, const std::set<gcmp::OwnerPtr<T>, Comparer>& value)
    {
        DBG_WARN(L"包含gcmp::OwnerPtr的set不支持直接赋值", L"GMEP", L"2016-09-14");
    }

    template<class K, class T>
    void AssignValue_DONT_USE(gcmp::NdbMap<K, gcmp::OwnerPtr<T> >& var, const std::map<K, gcmp::OwnerPtr<T> >& value)
    {
        DBG_WARN(L"包含值为gcmp::OwnerPtr的map不支持直接赋值", L"GMEP", L"2016-09-14");
    }

    template<class K, class T, class KeyComparer>
    void AssignValue_DONT_USE(gcmp::NdbMap<K, gcmp::OwnerPtr<T>, KeyComparer>& var, const std::map<K, gcmp::OwnerPtr<T>, KeyComparer>& value)
    {
        DBG_WARN(L"包含值为gcmp::OwnerPtr的map不支持直接赋值", L"GMEP", L"2016-09-14");
    }

    template<class K, class T>
    void AssignValue_DONT_USE(gcmp::NdbMap<K, T>& var, const std::map<K, T>& value)
    {
        var = value;
    }

    template<class K, class T, class KeyComparer>
    void AssignValue_DONT_USE(gcmp::NdbMap<K, T, KeyComparer>& var, const std::map<K, T, KeyComparer>& value)
    {
        var = value;
    }

    template<class K, class T, class Hash, class EqualTo = std::equal_to<K>>
    void AssignValue_DONT_USE(gcmp::NdbUnorderedMap<K, gcmp::OwnerPtr<T>, Hash, EqualTo>& var, const std::unordered_map<K, gcmp::OwnerPtr<T>, Hash, EqualTo>& value)
    {
        DBG_WARN(L"包含值为gcmp::OwnerPtr的unordered_map不支持直接赋值", L"GMEP", L"2016-09-14");
    }

    template<class K, class T, class Hash, class EqualTo = std::equal_to<K>>
    void AssignValue_DONT_USE(gcmp::NdbUnorderedMap<K, T, Hash, EqualTo>& var, const std::unordered_map<K, T, Hash, EqualTo>& value)
    {
        var = value;
    }
}


#define SERIALIZABLE_ENUM_CLASS(ENUM_TYPE)  \
    struct _NdbEnumClass##ENUM_TYPE : std::true_type{};  \
    enum class ENUM_TYPE :int32_t

#define DONT_CHECK_REGEN

#define DATA_ACCESSOR(RETURN_TYPE, NAME, GETTER, SETTER, RENGEN_CHECKER, BEHAVIOUR_TYPES)  \
    protected: RETURN_TYPE const& GETTER##__() const \
    {  \
        RENGEN_CHECKER; \
        return m_##NAME;  \
    }  \
    protected: RETURN_TYPE& GETTER##FW__() \
    {  \
        if ((BEHAVIOUR_TYPES) & gcmp::PBT_SupportTransaction)  \
        { \
            RENGEN_CHECKER; \
            gcmp::NdbDataChangeUtils::CheckTopOwnerObject(this); \
            gcmp::NdbDataChangeUtils::ValidatePropertyChange(this); \
            gcmp::NdbDataChangeUtils::NotifyElementPreChange(this); \
        } \
        return m_##NAME;  \
    }  \
    protected: void SETTER##__(const RETURN_TYPE& value)  \
    { \
        if ((BEHAVIOUR_TYPES) & gcmp::PBT_SupportTransaction)  \
        { \
            gcmp::NdbDataChangeUtils::CheckTopOwnerObject(this); \
            gcmp::NdbDataChangeUtils::ValidatePropertyChange(this); \
            gcmp::NdbDataChangeUtils::NotifyElementPreChange(this); \
        } \
        gcmp::AssignValue_DONT_USE(m_##NAME, value); \
        if ((BEHAVIOUR_TYPES) & gcmp::PBT_SupportTransaction)  \
        { \
            gcmp::NdbDataChangeUtils::NotifyElementPostChange(this); \
        } \
    } 

#define NDB_CONTAINER_RAW_DATA_GETTER(RETURN_TYPE, NAME, GETTER)  \
    protected: RETURN_TYPE const& GETTER##__() const \
    {  \
        return m_##NAME;  \
    }  \


#if defined(__linux__) && defined(__clang__)
#define OFFSET(structure, member) __builtin_offsetof(structure, member)
#else
#define OFFSET(structure, member) offsetof(structure, member)
#endif

#define TYPE_NAME(TYPE, NAME) gcmp::TypeDefTrait<TYPE>::RealName(NAME)

#define COMMA ,

#if defined(__linux__) && defined(__clang__)
#define CHECK_INT_TYPE(TYPE) \
    std::wstring realName = std::wstring(gcmp::TypeDefTrait<TYPE>::RealName(nullptr)); \
    GBMP_ASSERT(realName == std::wstring(L"int32_t") || realName == std::wstring(L"uint32_t") || \
                realName == std::wstring(L"int64_t") || realName == std::wstring(L"uint64_t") || \
                realName == std::wstring(L"int16_t") || realName == std::wstring(L"uint16_t") || \
                realName == std::wstring(L"int8_t") || realName == std::wstring(L"uint8_t")) \
    const wchar_t* typeName = WSTRING_OF(TYPE);\
    GBMP_ASSERT(typeName == L"UInt8" || typeName == L"UInt16" || \
        typeName == L"UInt32" || typeName == L"UInt64" || \
        typeName == L"Int8" || typeName == L"Int16" || \
        typeName == L"Int32" || typeName == L"Int64" || \
        typeName == L"float" || typeName == L"double");
#else

namespace gcmp 
{
    //字符串的静态比较方法
    constexpr bool IsEqualStr(wchar_t const *str1, wchar_t const *str2)
    {
        return (*str1 && *str2) ? (*str1 == *str2 && IsEqualStr(str1 + 1, str2 + 1)) : (!*str1 && !*str2);
    }
}

//不允许使用原生bool类型
#define CHECK_BOOL_TYPE(TYPE) \
    static_assert(!std::is_same<TYPE, bool>::value,"NDB: Need bool? just use Bool.");

//不允许使用不被支持的整数类型
#define CHECK_INT_TYPE(TYPE) \
    static_assert(gcmp::IsIntegral<TYPE>::value ? \
        gcmp::IsEqualStr(WSTRING_OF(TYPE), L"Int32") || gcmp::IsEqualStr(WSTRING_OF(TYPE), L"UInt32") || \
        gcmp::IsEqualStr(WSTRING_OF(TYPE), L"UInt8") || gcmp::IsEqualStr(WSTRING_OF(TYPE), L"UInt64") || \
        gcmp::IsEqualStr(WSTRING_OF(TYPE), L"Int8") || gcmp::IsEqualStr(WSTRING_OF(TYPE), L"Int16") || \
        gcmp::IsEqualStr(WSTRING_OF(TYPE), L"UInt16") || gcmp::IsEqualStr(WSTRING_OF(TYPE), L"Int64") \
        :true, \
    "NDB: Need integer? just use any one of'UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt64, Int64'.");

//如果要支持持久化,指针类型就只允许gcmp::IElement*、gcmp::IDocument*
#define CHECK_POINTER_TYPE(TYPE,BEHAVIOUR_TYPES) \
    static_assert(((BEHAVIOUR_TYPES) & (gcmp::PBT_SupportArchive)) ?  \
        std::is_pointer<TYPE>::value ?          \
            std::is_same<TYPE, gcmp::IDocument*>::value || std::is_same<TYPE, gcmp::IElement*>::value  \
            : true   \
        : true,    \
        "NDB : Need pointer ? just use gcmp::IDocument * or gcmp::IElement * ");
#endif

#define NDB_CHECK_RELATIONSHIP(CHILD, PARENT) \
    { static_assert(std::is_base_of<PARENT,CHILD>::value,#PARENT" is not base of "#CHILD); };

#ifdef _MSC_VER

#define NDB_CHECK_FULL_NAMESPACE(CLASS_NAME) \
    {\
        if ((std::string("class ") + std::string(#CLASS_NAME)) != typeid(CLASS_NAME).name())\
        {\
            GBMP_ASSERT(!"Please declare "#CLASS_NAME" with full namespace");\
        }\
    }

#else
#define NDB_CHECK_FULL_NAMESPACE(CLASS_NAME)
#endif

#define TURN_ON_SHOULD_ARCHIVE_CLASS_SCHEMA(T) \
    T::ms_pNdbClassSchema->SetShouldArchive(true);

#define TURN_OFF_SHOULD_ARCHIVE_CLASS_SCHEMA(T) \
    T::ms_pNdbClassSchema->SetShouldArchive(false);

// 不建议使用该宏，现在的使用处以后应该改掉。
#define MUTABLE_DATA_TRANSIENT(TYPE, NAME)  \
    MUTABLE_DATA_CUSTOM(TYPE, NAME, PBT_Transient)

// 不建议使用该宏，现在的使用处以后应该改掉。
#define MUTABLE_DATA_CUSTOM(TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: mutable TYPE m_##NAME; \
    DATA_ACCESSOR(TYPE, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, (BEHAVIOUR_TYPES));    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        CHECK_INT_TYPE(TYPE); \
        CHECK_POINTER_TYPE(TYPE,BEHAVIOUR_TYPES);\
        unsigned offset = OFFSET(Self, m_##NAME);    \
        static_assert(!(std::is_pointer<TYPE>::value  && !std::is_base_of<gcmp::NdbObjectSchematic, Self>::value), "Cannot have Raw pointer if your class is not derived from gcmp::NdbObjectSchematic"); \
        InferTypeAndAddPropertySchema((TYPE*)0, ms_pNdbClassSchema, WSTRING_OF(NAME), TYPE_NAME(TYPE, WSTRING_OF(TYPE)), offset, BEHAVIOUR_TYPES);


#define DATA_CUSTOM(TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: TYPE m_##NAME; \
    DATA_ACCESSOR(TYPE, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, BEHAVIOUR_TYPES);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        CHECK_BOOL_TYPE(TYPE); \
        CHECK_INT_TYPE(TYPE); \
        CHECK_POINTER_TYPE(TYPE,BEHAVIOUR_TYPES);\
        unsigned offset = OFFSET(Self, m_##NAME);    \
        static_assert(!(std::is_pointer<TYPE>::value  && !std::is_base_of<gcmp::NdbObjectSchematic, Self>::value), "Cannot have Raw pointer if your class is not derived from gcmp::NdbObjectSchematic"); \
        InferTypeAndAddPropertySchema((TYPE*)0, ms_pNdbClassSchema, WSTRING_OF(NAME), TYPE_NAME(TYPE, WSTRING_OF(TYPE)), offset, BEHAVIOUR_TYPES);


#define ENUM_DATA_CUSTOM(TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: TYPE m_##NAME; \
    DATA_ACCESSOR(TYPE, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, BEHAVIOUR_TYPES);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        static_assert(std::is_enum<TYPE>::value && _NdbEnumClass##TYPE::value, "Please use SERIALIZABLE_ENUM_CLASS to define enum class!"); \
        InferTypeAndAddPropertySchema((TYPE*)0, ms_pNdbClassSchema, WSTRING_OF(NAME), TYPE_NAME(TYPE, WSTRING_OF(TYPE)), offset, BEHAVIOUR_TYPES);


#define ARR_DATA_CUSTOM(TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbVector<TYPE> m_##NAME; \
    DATA_ACCESSOR(std::vector<TYPE>, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, BEHAVIOUR_TYPES);    \
    typedef gcmp::NdbVector<TYPE> NdbVector##NAME; \
    NDB_CONTAINER_RAW_DATA_GETTER(NdbVector##NAME, NAME, Get##NAME##AsNdbVector);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        CHECK_BOOL_TYPE(TYPE); \
        CHECK_INT_TYPE(TYPE); \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbVector<") + WSTRING_OF(TYPE) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Vector,\
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbVector<TYPE>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            TYPE_NAME(gcmp::NdbVector<TYPE>, typeName.c_str()), \
            offset, \
            BEHAVIOUR_TYPES \
        );

#define MAP_DATA_CUSTOM(KEY_TYPE, VALUE_TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbMap<KEY_TYPE, VALUE_TYPE> m_##NAME; \
    typedef std::map<KEY_TYPE COMMA VALUE_TYPE> Map##NAME; \
    DATA_ACCESSOR(Map##NAME, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, BEHAVIOUR_TYPES);    \
    typedef gcmp::NdbMap<KEY_TYPE COMMA VALUE_TYPE> NdbMap##NAME; \
    NDB_CONTAINER_RAW_DATA_GETTER(NdbMap##NAME, NAME, Get##NAME##AsNdbMap);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        if (gcmp::IsIntegral<KEY_TYPE>::value) { CHECK_INT_TYPE(KEY_TYPE); } \
        if (gcmp::IsIntegral<VALUE_TYPE>::value) { CHECK_INT_TYPE(VALUE_TYPE); } \
        if (std::is_same<KEY_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        if (std::is_same<VALUE_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbMap<") + WSTRING_OF(KEY_TYPE) +L"," + WSTRING_OF(VALUE_TYPE) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Map, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbMap<KEY_TYPE,VALUE_TYPE>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            TYPE_NAME(NdbMap##NAME, typeName.c_str()), \
            offset, \
            BEHAVIOUR_TYPES \
        );

#define MAP_DATA_WITH_COMPARER_CUSTOM(KEY_TYPE, VALUE_TYPE, KEY_COMPARER, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbMap<KEY_TYPE, VALUE_TYPE, KEY_COMPARER> m_##NAME; \
    typedef std::map<KEY_TYPE COMMA VALUE_TYPE COMMA KEY_COMPARER> Map##NAME; \
    DATA_ACCESSOR(Map##NAME, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, BEHAVIOUR_TYPES);    \
    typedef gcmp::NdbMap<KEY_TYPE COMMA VALUE_TYPE COMMA KEY_COMPARER> NdbMap##NAME; \
    NDB_CONTAINER_RAW_DATA_GETTER(NdbMap##NAME, NAME, Get##NAME##AsNdbMap);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        if (gcmp::IsIntegral<KEY_TYPE>::value) { CHECK_INT_TYPE(KEY_TYPE); } \
        if (gcmp::IsIntegral<VALUE_TYPE>::value) { CHECK_INT_TYPE(VALUE_TYPE); } \
        if (std::is_same<KEY_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        if (std::is_same<VALUE_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbMap<") + WSTRING_OF(KEY_TYPE) + L"," + WSTRING_OF(VALUE_TYPE) + L"," + WSTRING_OF(KEY_COMPARER) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Map, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbMap<KEY_TYPE,VALUE_TYPE, KEY_COMPARER>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            typeName.c_str(), \
            offset, \
            BEHAVIOUR_TYPES \
        );

#define UMAP_DATA_CUSTOM(KEY_TYPE, VALUE_TYPE, HASH_FUNC, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbUnorderedMap<KEY_TYPE, VALUE_TYPE, HASH_FUNC> m_##NAME; \
    typedef std::unordered_map<KEY_TYPE COMMA VALUE_TYPE COMMA HASH_FUNC> UMap##NAME; \
    DATA_ACCESSOR(UMap##NAME, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, BEHAVIOUR_TYPES);    \
    typedef gcmp::NdbUnorderedMap<KEY_TYPE COMMA VALUE_TYPE COMMA HASH_FUNC> NdbUnorderedMapMap##NAME; \
    NDB_CONTAINER_RAW_DATA_GETTER(NdbUnorderedMapMap##NAME, NAME, Get##NAME##AsNdbUnorderedMap);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        if (gcmp::IsIntegral<KEY_TYPE>::value) { CHECK_INT_TYPE(KEY_TYPE); } \
        if (gcmp::IsIntegral<VALUE_TYPE>::value) { CHECK_INT_TYPE(VALUE_TYPE); } \
        if (std::is_same<KEY_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        if (std::is_same<VALUE_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbUnorderedMap<") + WSTRING_OF(KEY_TYPE) +L"," + WSTRING_OF(VALUE_TYPE) + L"," + WSTRING_OF(HASH_FUNC) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::UnorderedMap, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbUnorderedMap<KEY_TYPE,VALUE_TYPE, HASH_FUNC>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            TYPE_NAME(NdbUnorderedMapMap##NAME, typeName.c_str()),  \
            offset, \
            BEHAVIOUR_TYPES \
        );

#define UMAP_DATA_WITH_EQUALTO_CUSTOM(KEY_TYPE, VALUE_TYPE, HASH_FUNC, KEY_EQUAL, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbUnorderedMap<KEY_TYPE, VALUE_TYPE, HASH_FUNC, KEY_EQUAL> m_##NAME; \
    typedef std::unordered_map<KEY_TYPE COMMA VALUE_TYPE COMMA HASH_FUNC COMMA KEY_EQUAL> UMap##NAME; \
    DATA_ACCESSOR(UMap##NAME, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, BEHAVIOUR_TYPES);    \
    typedef gcmp::NdbUnorderedMap<KEY_TYPE COMMA VALUE_TYPE COMMA HASH_FUNC COMMA KEY_EQUAL> NdbUnorderedMapMap##NAME; \
    NDB_CONTAINER_RAW_DATA_GETTER(NdbUnorderedMapMap##NAME, NAME, Get##NAME##AsNdbUnorderedMap);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        if (gcmp::IsIntegral<KEY_TYPE>::value) { CHECK_INT_TYPE(KEY_TYPE); } \
        if (gcmp::IsIntegral<VALUE_TYPE>::value) { CHECK_INT_TYPE(VALUE_TYPE); } \
        if (std::is_same<KEY_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        if (std::is_same<VALUE_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbUnorderedMap<") + WSTRING_OF(KEY_TYPE) +L"," + WSTRING_OF(VALUE_TYPE) + L"," + WSTRING_OF(HASH_FUNC) + WSTRING_OF(KEY_EQUAL) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::UnorderedMap, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbUnorderedMap<KEY_TYPE,VALUE_TYPE, HASH_FUNC, KEY_EQUAL>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            TYPE_NAME(NdbUnorderedMapMap##NAME, typeName.c_str()),  \
            offset, \
            BEHAVIOUR_TYPES \
        );

#define SET_DATA_CUSTOM(TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbSet<TYPE> m_##NAME; \
    DATA_ACCESSOR(std::set<TYPE>, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, BEHAVIOUR_TYPES);    \
    NDB_CONTAINER_RAW_DATA_GETTER(gcmp::NdbSet<TYPE>, NAME, Get##NAME##AsNdbSet);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        CHECK_BOOL_TYPE(TYPE); \
        CHECK_INT_TYPE(TYPE); \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbSet<") + WSTRING_OF(TYPE) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Set, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbSet<TYPE>), \
            ms_pNdbClassSchema, WSTRING_OF(NAME), \
            TYPE_NAME(gcmp::NdbSet<TYPE>, typeName.c_str()), \
            offset, \
            BEHAVIOUR_TYPES \
        );

#define SET_DATA_WITH_COMPARER_CUSTOM(TYPE, COMPARER, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbSet<TYPE, COMPARER> m_##NAME; \
    typedef std::set<TYPE, COMPARER> __NdbSet##NAME##Type; \
    DATA_ACCESSOR(__NdbSet##NAME##Type, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, BEHAVIOUR_TYPES);    \
    NDB_CONTAINER_RAW_DATA_GETTER(__NdbSet##NAME##Type, NAME, Get##NAME##AsNdbSet);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        CHECK_BOOL_TYPE(TYPE); \
        CHECK_INT_TYPE(TYPE); \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbSet<") + WSTRING_OF(TYPE) + std::wstring(L",") + WSTRING_OF(COMPARER) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Set, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbSet<TYPE, COMPARER>), \
            ms_pNdbClassSchema, WSTRING_OF(NAME), \
            typeName.c_str(), \
            offset, \
            BEHAVIOUR_TYPES \
        );

#define DATA_REGEN_CUSTOM(TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }  \
    private: TYPE m_##NAME; \
    DECLARE_REGEN_FUNCTIONS(NAME)    \
    DATA_ACCESSOR(TYPE, NAME, Get##NAME, Set##NAME, ValidateRegenData##NAME##Use(), BEHAVIOUR_TYPES); \
    private: static void AddNdb##NAME##Property()   \
    {   \
        CHECK_BOOL_TYPE(TYPE); \
        CHECK_INT_TYPE(TYPE); \
        CHECK_POINTER_TYPE(TYPE,BEHAVIOUR_TYPES);\
        unsigned offset = OFFSET(Self, m_##NAME);    \
        static_assert(!(std::is_pointer<TYPE>::value  && !std::is_base_of<gcmp::NdbObjectSchematic, Self>::value), "Cannot have Raw pointer if your class is not derived from gcmp::NdbObjectSchematic"); \
        InferTypeAndAddPropertySchema((TYPE*)0, ms_pNdbClassSchema, WSTRING_OF(NAME), TYPE_NAME(TYPE, WSTRING_OF(TYPE)), offset, BEHAVIOUR_TYPES);


#define ENUM_DATA_REGEN_CUSTOM(TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }  \
    private: TYPE m_##NAME; \
    DECLARE_REGEN_FUNCTIONS(NAME)    \
    DATA_ACCESSOR(TYPE, NAME, Get##NAME, Set##NAME, ValidateRegenData##NAME##Use(), BEHAVIOUR_TYPES); \
    private: static void AddNdb##NAME##Property()   \
    {   \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        static_assert(std::is_enum<TYPE>::value && _NdbEnumClass##TYPE::value, "Please use SERIALIZABLE_ENUM_CLASS to define enum class!"); \
        InferTypeAndAddPropertySchema((TYPE*)0, ms_pNdbClassSchema, WSTRING_OF(NAME), TYPE_NAME(TYPE, WSTRING_OF(TYPE)), offset, BEHAVIOUR_TYPES);

#define DATA_REGEN_CUSTOM_OVERRIDE(TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }  \
    private: TYPE m_##NAME; \
    DECLARE_OVERRIDE_REGEN_FUNCTIONS(NAME)    \
    DATA_ACCESSOR(TYPE, NAME, Get##NAME, Set##NAME, ValidateRegenData##NAME##Use(), BEHAVIOUR_TYPES); \
    private: static void AddNdb##NAME##Property()   \
    {   \
        CHECK_BOOL_TYPE(TYPE); \
        CHECK_INT_TYPE(TYPE); \
        CHECK_POINTER_TYPE(TYPE,BEHAVIOUR_TYPES);\
        unsigned offset = OFFSET(Self, m_##NAME);    \
        InferTypeAndAddPropertySchema((TYPE*)0, ms_pNdbClassSchema, WSTRING_OF(NAME), TYPE_NAME(TYPE, WSTRING_OF(TYPE)), offset, BEHAVIOUR_TYPES);


#define ARR_DATA_REGEN_CUSTOM(TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbVector<TYPE> m_##NAME; \
    DECLARE_REGEN_FUNCTIONS(NAME)    \
    DATA_ACCESSOR(std::vector<TYPE>, NAME, Get##NAME, Set##NAME, ValidateRegenData##NAME##Use(), BEHAVIOUR_TYPES);    \
    NDB_CONTAINER_RAW_DATA_GETTER(gcmp::NdbVector<TYPE>, NAME, Get##NAME##AsNdbVector);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        CHECK_BOOL_TYPE(TYPE); \
        CHECK_INT_TYPE(TYPE); \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbVector<") + WSTRING_OF(TYPE) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Vector, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbVector<TYPE>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            TYPE_NAME(gcmp::NdbVector<TYPE>, typeName.c_str()), \
            offset, \
            BEHAVIOUR_TYPES \
        );

#define MAP_DATA_REGEN_CUSTOM(KEY_TYPE, VALUE_TYPE, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbMap<KEY_TYPE, VALUE_TYPE> m_##NAME; \
    typedef std::map<KEY_TYPE COMMA VALUE_TYPE> Map##NAME; \
    DECLARE_REGEN_FUNCTIONS(NAME)    \
    DATA_ACCESSOR(Map##NAME, NAME, Get##NAME, Set##NAME, ValidateRegenData##NAME##Use(), BEHAVIOUR_TYPES);    \
    typedef gcmp::NdbMap<KEY_TYPE COMMA VALUE_TYPE> NdbMap##NAME; \
    NDB_CONTAINER_RAW_DATA_GETTER(NdbMap##NAME, NAME, Get##NAME##AsNdbMap);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        if (gcmp::IsIntegral<KEY_TYPE>::value) { CHECK_INT_TYPE(KEY_TYPE); } \
        if (gcmp::IsIntegral<VALUE_TYPE>::value) { CHECK_INT_TYPE(VALUE_TYPE); } \
        if (std::is_same<KEY_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        if (std::is_same<VALUE_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbUnorderedMap<") + WSTRING_OF(KEY_TYPE) +L"," + WSTRING_OF(VALUE_TYPE) + L"," + WSTRING_OF(HASH_FUNC) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Map, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbMap<KEY_TYPE,VALUE_TYPE>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            TYPE_NAME(gcmp::NdbMap<KEY_TYPE COMMA VALUE_TYPE>, typeName.c_str()), \
            offset, \
            BEHAVIOUR_TYPES \
        );

#define UMAP_DATA_REGEN_CUSTOM(KEY_TYPE, VALUE_TYPE, HASH_FUNC, NAME, BEHAVIOUR_TYPES)  \
        AddNdb##NAME##Property();  \
    }   \
    private: gcmp::NdbUnorderedMap<KEY_TYPE, VALUE_TYPE, HASH_FUNC> m_##NAME; \
    typedef std::unordered_map<KEY_TYPE COMMA VALUE_TYPE COMMA HASH_FUNC> UMap##NAME; \
    DECLARE_REGEN_FUNCTIONS(NAME)    \
    DATA_ACCESSOR(UMap##NAME, NAME, Get##NAME, Set##NAME, ValidateRegenData##NAME##Use(), BEHAVIOUR_TYPES);    \
    typedef gcmp::NdbUnorderedMap<KEY_TYPE COMMA VALUE_TYPE COMMA HASH_FUNC> NdbUnorderedMapMap##NAME; \
    NDB_CONTAINER_RAW_DATA_GETTER(NdbUnorderedMapMap##NAME, NAME, Get##NAME##AsNdbUnorderedMap);    \
    private: static void AddNdb##NAME##Property()   \
    {   \
        if (gcmp::IsIntegral<KEY_TYPE>::value) { CHECK_INT_TYPE(KEY_TYPE); } \
        if (gcmp::IsIntegral<VALUE_TYPE>::value) { CHECK_INT_TYPE(VALUE_TYPE); } \
        if (std::is_same<KEY_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        if (std::is_same<VALUE_TYPE, bool>::value) { GBMP_ASSERT(!"Please use Bool instead") } \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbUnorderedMap<") + WSTRING_OF(KEY_TYPE) +L"," + WSTRING_OF(VALUE_TYPE) + L"," + WSTRING_OF(HASH_FUNC) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::UnorderedMap, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(gcmp::NdbUnorderedMap<KEY_TYPE,VALUE_TYPE,HASH_FUNC>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            TYPE_NAME(NdbUnorderedMapMap##NAME, typeName.c_str()), \
            offset, \
            BEHAVIOUR_TYPES \
        );

#pragma region 临时对象专用宏定义，所有功能框架都不支持，也不检查数据类型

#define DATA_WITHOUT_CHECK_AND_SUPPORT(TYPE, NAME)  \
        AddNdb##NAME##Property();  \
    }   \
    private: TYPE m_##NAME; \
    DATA_ACCESSOR(TYPE, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, gcmp::PBT_Transient);    \
    private: static void AddNdb##NAME##Property()   \
    { \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        InferTypeAndAddPropertySchema((TYPE*)0, ms_pNdbClassSchema, WSTRING_OF(NAME), TYPE_NAME(TYPE, WSTRING_OF(TYPE)), offset, gcmp::PBT_Transient);

#define MUTABLE_DATA_WITHOUT_CHECK_AND_SUPPORT(TYPE, NAME)  \
        AddNdb##NAME##Property();  \
    }   \
    private: mutable TYPE m_##NAME; \
    DATA_ACCESSOR(TYPE, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, gcmp::PBT_Transient);    \
    private: static void AddNdb##NAME##Property()   \
    { \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        InferTypeAndAddPropertySchema((TYPE*)0, ms_pNdbClassSchema, WSTRING_OF(NAME), TYPE_NAME(TYPE, WSTRING_OF(TYPE)), offset, gcmp::PBT_Transient);

#define WEAKPTR_DATA_WITHOUT_CHECK_AND_SUPPORT(TYPE, NAME)  \
        AddNdb##NAME##Property();  \
    }   \
    private: TYPE m_##NAME; \
    DATA_ACCESSOR(TYPE, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, gcmp::PBT_Transient);    \
    private: static void AddNdb##NAME##Property()   \
    { 

#define ARR_DATA_WITHOUT_CHECK_AND_SUPPORT(TYPE, NAME)  \
        AddNdb##NAME##Property();  \
    }   \
    private: std::vector<TYPE> m_##NAME; \
    DATA_ACCESSOR(std::vector<TYPE>, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, gcmp::PBT_Transient);    \
    private: static void AddNdb##NAME##Property()   \
    { \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbVector<") + WSTRING_OF(TYPE) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Vector,\
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(std::vector<TYPE>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            TYPE_NAME(gcmp::NdbVector<TYPE>, typeName.c_str()), \
            offset, \
            gcmp::PBT_Transient \
        );

#define SET_DATA_WITHOUT_CHECK_AND_SUPPORT(TYPE, NAME)  \
        AddNdb##NAME##Property();  \
    }   \
    private: std::set<TYPE> m_##NAME; \
    DATA_ACCESSOR(std::set<TYPE>, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, gcmp::PBT_Transient);    \
    private: static void AddNdb##NAME##Property()   \
    { \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbSet<") + WSTRING_OF(KEY_TYPE) + L"," + WSTRING_OF(VALUE_TYPE) + L"," + WSTRING_OF(KEY_COMPARER) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Map, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(std::set<TYPE>), \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            typeName.c_str(), \
            offset, \
            gcmp::PBT_Transient \
        );

#define SET_DATA_WITH_COMPARER_WITHOUT_CHECK_AND_SUPPORT(TYPE, COMPARER, NAME)  \
        AddNdb##NAME##Property();  \
    }   \
    private: std::set<TYPE, COMPARER> m_##NAME; \
    typedef std::set<TYPE, COMPARER> __NdbSet##NAME##Type; \
    DATA_ACCESSOR(__NdbSet##NAME##Type, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, gcmp::PBT_Transient);    \
    private: static void AddNdb##NAME##Property()   \
    { \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbSet<") + WSTRING_OF(TYPE) + std::wstring(L",") + WSTRING_OF(COMPARER) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Set, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(std::set<TYPE, COMPARER>), \
            ms_pNdbClassSchema, WSTRING_OF(NAME), \
            typeName.c_str(), \
            offset, \
            gcmp::PBT_Transient \
        );

#define MAP_DATA_WITHOUT_CHECK_AND_SUPPORT(KEY_TYPE, VALUE_TYPE, NAME)  \
        AddNdb##NAME##Property();  \
    }   \
    private: std::map<KEY_TYPE, VALUE_TYPE> m_##NAME; \
    typedef std::map<KEY_TYPE COMMA VALUE_TYPE> Map##NAME; \
    DATA_ACCESSOR(Map##NAME, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, gcmp::PBT_Transient);    \
    private: static void AddNdb##NAME##Property()   \
    { \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbMap<") + WSTRING_OF(KEY_TYPE) +L"," + WSTRING_OF(VALUE_TYPE) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Map, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(std::map<KEY_TYPE, VALUE_TYPE>)/*这个宏目的是不检查类型，所以不能用KEY_TYPE、VALUE_TYPE去尝试匹配模板。只需要知道大小就好*/, \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            typeName.c_str(), \
            offset, \
            gcmp::PBT_Transient \
        );

#define MAP_DATA_WITH_COMPARER_WITHOUT_CHECK_AND_SUPPORT(KEY_TYPE, VALUE_TYPE, KEY_COMPARER, NAME)  \
        AddNdb##NAME##Property();  \
    }   \
    private: std::map<KEY_TYPE, VALUE_TYPE, KEY_COMPARER> m_##NAME; \
    typedef std::map<KEY_TYPE COMMA VALUE_TYPE COMMA KEY_COMPARER> Map##NAME; \
    DATA_ACCESSOR(Map##NAME, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, gcmp::PBT_Transient);    \
    private: static void AddNdb##NAME##Property()   \
    { \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbMap<") + WSTRING_OF(KEY_TYPE) + L"," + WSTRING_OF(VALUE_TYPE) + L"," + WSTRING_OF(KEY_COMPARER) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::Map, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(std::map<KEY_TYPE, VALUE_TYPE, KEY_COMPARER>)/*这个宏目的是不检查类型，所以不能用KEY_TYPE、VALUE_TYPE去尝试匹配模板。只需要知道大小就好*/, \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            typeName.c_str(), \
            offset, \
            gcmp::PBT_Transient \
        );

#define UMAP_DATA_WITHOUT_CHECK_AND_SUPPORT(KEY_TYPE, VALUE_TYPE, HASH_FUNC, NAME)  \
        AddNdb##NAME##Property();  \
    }   \
    private: std::unordered_map<KEY_TYPE, VALUE_TYPE, HASH_FUNC> m_##NAME; \
    typedef std::unordered_map<KEY_TYPE COMMA VALUE_TYPE COMMA HASH_FUNC> UMap##NAME; \
    DATA_ACCESSOR(UMap##NAME, NAME, Get##NAME, Set##NAME, DONT_CHECK_REGEN, gcmp::PBT_Transient);    \
    private: static void AddNdb##NAME##Property()   \
    { \
        unsigned offset = OFFSET(Self, m_##NAME);    \
        std::wstring typeName = std::wstring(L"gcmp::NdbUnorderedMap<") + WSTRING_OF(KEY_TYPE) +L"," + WSTRING_OF(VALUE_TYPE) + L"," + WSTRING_OF(HASH_FUNC) + L">"; \
        gcmp::NdbClassSchema::AddPropertySchema( \
            gcmp::NdbPropertySchemaType::UnorderedMap, \
            gcmp::IntOrFloatType::UnKnown, \
            gcmp::IntBaseType::UnKnown, \
            sizeof(std::unordered_map<KEY_TYPE, VALUE_TYPE, HASH_FUNC>)/*这个宏目的是不检查类型，所以不能用KEY_TYPE、VALUE_TYPE去尝试匹配模板。只需要知道大小就好*/, \
            ms_pNdbClassSchema, \
            WSTRING_OF(NAME), \
            typeName.c_str(), \
            offset, \
            gcmp::PBT_Transient \
        );

#define IS_GCMP_TYPE(TYPE)

#pragma endregion

///////////////////////////////////////////////////////////
//数据定义宏
#define DATA(TYPE, NAME)  \
    DATA_CUSTOM(TYPE, NAME, gcmp::PBT_SupportAll)

#define DATA_TRANSIENT(TYPE, NAME)  \
    DATA_CUSTOM(TYPE, NAME, gcmp::PBT_Transient)

#define ENUM_DATA(TYPE, NAME)  \
    ENUM_DATA_CUSTOM(TYPE, NAME, gcmp::PBT_SupportAll)

#define ENUM_DATA_TRANSIENT(TYPE, NAME)  \
    ENUM_DATA_CUSTOM(TYPE, NAME, gcmp::PBT_Transient)

#define ARR_DATA(TYPE, NAME)  \
    ARR_DATA_CUSTOM(TYPE, NAME, gcmp::PBT_SupportAll)

#define ARR_DATA_TRANSIENT(TYPE, NAME)  \
    ARR_DATA_CUSTOM(TYPE, NAME, gcmp::PBT_Transient)

#define SET_DATA(TYPE, NAME)  \
    SET_DATA_CUSTOM(TYPE, NAME, gcmp::PBT_SupportAll)

#define SET_DATA_TRANSIENT(TYPE, NAME)  \
    SET_DATA_CUSTOM(TYPE, NAME, gcmp::PBT_Transient)

#define SET_DATA_WITH_COMPARER(TYPE, COMPARER, NAME)  \
    SET_DATA_WITH_COMPARER_CUSTOM(TYPE, COMPARER, NAME, gcmp::PBT_SupportAll)

#define SET_DATA_WITH_COMPARER_TRANSIENT(TYPE, COMPARER, NAME)  \
    SET_DATA_WITH_COMPARER_CUSTOM(TYPE, COMPARER, NAME, gcmp::PBT_Transient)

#define MAP_DATA(KEY_TYPE, VALUE_TYPE, NAME)  \
    MAP_DATA_CUSTOM(KEY_TYPE, VALUE_TYPE, NAME, gcmp::PBT_SupportAll)

#define MAP_DATA_WITH_COMPARER(KEY_TYPE, VALUE_TYPE, KEY_COMPARER, NAME)  \
    MAP_DATA_WITH_COMPARER_CUSTOM(KEY_TYPE, VALUE_TYPE, KEY_COMPARER, NAME, gcmp::PBT_SupportAll)

#define MAP_DATA_TRANSIENT(KEY_TYPE, VALUE_TYPE, NAME)  \
    MAP_DATA_CUSTOM(KEY_TYPE, VALUE_TYPE, NAME, gcmp::PBT_Transient)

#define MAP_DATA_WITH_COMPARER_TRANSIENT(KEY_TYPE, VALUE_TYPE, KEY_COMPARER, NAME)  \
    MAP_DATA_WITH_COMPARER_CUSTOM(KEY_TYPE, VALUE_TYPE, KEY_COMPARER, NAME, gcmp::PBT_Transient)

#define UMAP_DATA(KEY_TYPE, VALUE_TYPE, HASH_FUNC, NAME)  \
    UMAP_DATA_CUSTOM(KEY_TYPE, VALUE_TYPE, HASH_FUNC, NAME, gcmp::PBT_SupportAll)

#define UMAP_DATA_WITH_EQUALTO(KEY_TYPE, VALUE_TYPE, HASH_FUNC, KEY_EQUAL, NAME)  \
    UMAP_DATA_WITH_EQUALTO_CUSTOM(KEY_TYPE, VALUE_TYPE, HASH_FUNC, KEY_EQUAL, NAME, gcmp::PBT_SupportAll)

#define UMAP_DATA_TRANSIENT(KEY_TYPE, VALUE_TYPE, HASH_FUNC, NAME)  \
    UMAP_DATA_CUSTOM(KEY_TYPE, VALUE_TYPE, HASH_FUNC, NAME, gcmp::PBT_Transient)

#define NDB_DECLARE_CLONEABLE_OBJECT(NS_PREFIX, CLASS_NAME, PARENT_NAME,CLASS_ID,MODULE_NS_PREFIX) \
    DATA_BEGIN(NS_PREFIX, CLASS_NAME, PARENT_NAME,CLASS_ID,MODULE_NS_PREFIX) \
    DATA_END 

#define NDB_DEFINE_CLONEABLE_OBJECT(CLASS_NAME) \
    DATA_DEFINE(CLASS_NAME)

#define DATA_BEGIN_IMP(NS_PREFIX, CLASS_NAME, PARENT_NAME, CLASS_ID, MODULE_NS_PREFIX)               \
public:                                                     \
    static const gcmp::ClassId & GetClassId() { static gcmp::ClassId s_classId(WSTRING_OF(CLASS_ID)); return s_classId; }  \
    typedef PARENT_NAME Super; \
    typedef CLASS_NAME Self; \
    static gcmp::NdbClassSchema* ms_pNdbClassSchema; \
    virtual const gcmp::NdbClassSchema* GetClassSchema() const override { return ms_pNdbClassSchema; }   \
    FORCE_INLINE static gcmp::NdbClassSchema* GetStaticClassSchema() { return ms_pNdbClassSchema; }   \
    static gcmp::OwnerPtr<gcmp::NdbObject> CreateNdbObject();    \
    void InitializeObject();     \
    virtual bool CheckClassInheritanceRelationship() const override \
    { \
        if (std::is_abstract<Self>::value) \
            return true; \
        return (void*)this == (void*)((Super*)this); \
    } \
    CLASS_NAME(); \
    static void AddNdbSchema() \
    {   \
        NDB_CHECK_RELATIONSHIP(NS_PREFIX::CLASS_NAME, PARENT_NAME);\
        NDB_CHECK_FULL_NAMESPACE(NS_PREFIX::CLASS_NAME);\
        NDB_CHECK_FULL_NAMESPACE(PARENT_NAME);\
        ms_pNdbClassSchema = gcmp::NdbClassSchemaLibraryOfCode::GetInstance()->CreateClassSchema( \
            Super::GetClassId(), \
            GetClassId(), \
            WSTRING_OF(PARENT_NAME), \
            WSTRING_OF(CLASS_NAME), \
            CLASS_NAME::CreateNdbObject, \
            MODULE_NS_PREFIX::MODULE_ID, \
            TR_MODULE_NAME, \
            sizeof(CLASS_NAME)\
        ); 

#define IDB_DATA_BEGIN_IMP(NS_PREFIX, CLASS_NAME, PARENT_NAME, CLASS_ID, MODULE_NS_PREFIX) \
    public:\
    virtual Guid GetClassGuid(void) const override; \
    virtual gcmp::ISerializationBehavior * GetSerializationBehavior(void) const override; \
    virtual gcmp::ICloneBehavior * GetCloneBehavior(void) const override; \
    DATA_BEGIN_IMP(NS_PREFIX, CLASS_NAME, PARENT_NAME, CLASS_ID, MODULE_NS_PREFIX)
